// commands.c
// Joseph Craig, Rebecca Rush, Steven Andryzcik
// Version 6 - 12/08/2011

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "commands.h"
#include "common.h"
#include "mpx_supt.h"
#include "process.h"

void kill_process(char *);
void suspend_process(char *);

/*********************************** Macros ***********************************/
#define DEFAULT_DIRECTORY "."

/****************************** Global Variables ******************************/
char *command_names[NUM_FUNCTIONS] = {
	// R1 Commands
	"Help",
	"Version",
	"Display_Date",
	"Display_Date_Word",
	"Change_Date",
	"Directory",
	"Quit",
	
	// R2 Commands
	"Suspend",
	"Resume",
	"Set_Priority",
	"Show_PCB",
	"Show_All",
	"Show_Ready",
	"Show_Blocked",
	"Show_Suspended",
	
	// R4 Commands
	"Load",
	"Terminate"
	
};

date_rec *date_p;

int quitting = FALSE;

void (*commands[NUM_FUNCTIONS])(int, char **) = {
	// R1 Commands
	Help,
	Version,
	Display_Date,
	Display_Date_Word,
	Change_Date,
	Directory,
	Quit,
	
	// R2 Commands
	Suspend,
	Resume,
	Set_Priority,
	Show_PCB,
	Show_All,
	Show_Ready,
	Show_Blocked,
	Show_Suspended,
	
	// R4 Commands
	Load,
	Terminate
};

/**************************** Command Definitions *****************************/
// Change_Date
// Parameters: Number of arguments, Array of day, month, and year
// Returns: void
// Functions called: print, atoi, is_leap_year, save_date,
// sys_set_date, sprintf
// Sets the date to the variables that are passed to it.
void Change_Date(int argc, char **argv) {
	date_rec
		reset,
		save_date;
	int
		day,
		month,
		temp;
	long year;
	
	/* Validate the arguments.
	 */
	if (argc != 3) {
		print(
			"\n\tChange_Date takes three arguments in the order 'month' 'day' "
			"year.\n\n",
			TRUE
		);
		return;
	}
	
	month = atoi(argv[0]);
	day   = atoi(argv[1]);
	year  = atol(argv[2]);
	
	if (month < 1 || month > 12) {
		print("\n\tThe month must be a number between 1 and 12.\n\n", TRUE);
	}
	
	else if (day < 1 || day > 31) {
		print("\n\tThe day must be a number between 1 and 31.\n\n", TRUE);
	}
	
	else if (year < 1 || year > 9999) {
		print("\n\tThe year must be a number between 1 and 9999.\n\n", TRUE);
	}
	
	else if (
		day == 31 && (
			month == 4 || month == 6 || month == 9 || month == 11
		)
	) {
		print("\n\tThere are only 30 days in the requested month.\n\n", TRUE);
	}
	
	else if (
		month == 2 && (
			(is_leap_year(year) && day > 29) ||
			day > 28
		)
	) {
		if (is_leap_year(year)) {
			print(
				"\n\tThere are only 29 days in February for the requested "
				"year.\n\n",
				TRUE
			);
		} else {
			print(
				"\n\tThere are only 28 days in February for the requested "
				"year.\n\n",
				TRUE
			);
		}
	}
	
	/* The arguments are valid, so attempt to set the date.
	 */
	else {
		save_date.day   = day;
		save_date.month = month;
		save_date.year  = year;
		
		temp = sys_set_date(&save_date);
		
		if (temp != 0) {
			print("\n\tAn error occurred while trying to set the date.\n\n",
					TRUE
			);
		} else {
			char buffer[BUFSIZ] = "";
			sprintf(
				buffer,
				"\n\tYou have successfully set the date to %d/%d/%d.\n\n",
				month,
				day,
				year
			);
			print(buffer, TRUE);
		}
	}
}

// Directory
// Parameters: argc, argv - not needed for this function.
// Returns: void
// Functions called: sys_open_dir, print, sys_get_entry, sprintf, strlen,
// sys_close_dir
// First uses the system function sys_open_dir to open the directory
// then uses the system function sys_get_entry to pull the different
// files and check to see if they have the mpx extension
// Lists all files with mpx extensions.
void Directory(int argc, char **argv) {
	char
		filename[BUFSIZ] = "",
		printout[BUFSIZ] = "\n",
		*directory;
	int result;
	
	/* Determine that the user has supplied a legal number of arguments.
	 */
	if (argc > 1) {
		print("\n\tUsage: Directory {path}\n\n", TRUE);
		return;
	}
	
	// Attempt to open the user's requested directory.
	directory = (argc == 0 ? DEFAULT_DIRECTORY : argv[0]);
	result = sys_open_dir(directory);
	
	// If the attempt succeeded, print the files in the directory and their
	// sizes to the standard output.
	if (result == OK) {
		long file_size;

		while (
			sys_get_entry(
				filename,
				BUFSIZ,
				&file_size
			) != ERR_SUP_NOENTR
		) {
			sprintf(
				printout,
				"\t%-20s %d bytes\n",
				filename,
				file_size
			);
			print(printout, FALSE);
		}

		// If there were no files written, write a status message.
		if (strlen(filename) == 0  ) {
			print("\n\tNo .MPX files were found in the Directory.\n\n", TRUE);
			
		} else {
			print("\n", TRUE);
		}
		
		// Close the directory.
		sys_close_dir();
	}

	// Otherwise, print error messages explaining the failure.
	else {
		switch (result) {
			case ERR_SUP_DIROPN:
				sprintf(
					printout,
					"\n\tError: could not open the Directory \"%s\".\n\n",
					directory
				);
				break;

			case ERR_SUP_INVDIR:
				sprintf(
					printout,
					"\n\tError: \"%s\" is an invalid Directory.\n\n",
					directory
				);
				break;
		}
		
		print(printout, TRUE);
	}
}

// Display_Date
// Parameters: argc, argv - neither are used to make this function work.
// Returns: void
// Functions called: sys_get_date, sprintf, print
// Displays the current date in numerical format.
void Display_Date(int argc, char **argv) {
	char buffer[BUFSIZ] = "";

	argc = argc;
    argv = argv;
	
	sys_get_date(date_p);
	sprintf(buffer, "%d/%d/%d\n", date_p->month, date_p->day, date_p->year);
	
	print(buffer, TRUE);
}

// Display_Date_Word
// Parameters: argc, argv - neither are used to make this function work.
// Returns: void
// Functions called: sys_get_date, sprintf, print
// Is the same as displayDate(), it just converts the month
// into the word for that month
void Display_Date_Word (int argc, char **argv) {
	char
		buffer[BUFSIZ] = "",
		*month;
	
	argc = argc;
    argv = argv;
	
	sys_get_date(date_p);
	
	if (date_p->month == 1)
		month = "January";
    else if (date_p->month == 2)
		month = "February";
    else if(date_p->month == 3)
		month = "March";
    else if(date_p->month == 4)
		month = "April";
    else if(date_p->month == 5)
		month = "May";
    else if(date_p->month == 6)
		month = "June";
    else if(date_p->month == 7)
		month = "July";
    else if(date_p->month == 8)
		month = "August";
    else if(date_p->month == 9)
		month = "September";
    else if(date_p->month == 10)
		month = "October";
    else if(date_p->month == 11)
		month = "November";
    else if(date_p->month == 12)
		month = "December";
	
	sprintf(
		buffer,
		"The Current Date Set:\n"
		"MONTH:  %s\n"
		"DAY:    %d\n"
		"YEAR:   %d\n",
		month,
		date_p->day,
		date_p->year
	);
	print(buffer, TRUE);
}

// Help
// Paramaters: argc, argv
// Returns: void
// Functions called: print, print_file, strlen, strcpy
// This aglorithm uses selection statements to determine
// if they are trying to access the main help function or
// the help function for individual functions.
// Lists all functions available in the system
// or displays information on a specifc function.
void Help(int argc, char **argv) {
	char
		filename[18] = "Help\\Help.txt",
		ending[] = ".txt";
	int return_code;

	/* If there are more than two arguments, then the function was called
	 * improperly.
	 */
	if (argc > 1) {
		print(
			"\n\tThis function takes zero or one argument. Type \"Help\" for "
			"more information.\n\n",
			TRUE
		);
		
		return;
	}
	
	/* If the command was passed an argument, build the look-up filename that
	 * the command would have.
	 */
	if (argc == 1) {
		if (strlen(argv[0]) <= 8) {
			strcpy(filename + 5, argv[0]);
		} else {
			int i;
			
			for (i = 0; i < 6; i++) {
				filename[i + 5] = argv[0][i];
				filename[i + 6] = '\0';
			}
			
			strcat(filename, "~1");
		}
		
		strcat(filename, ending);
	}
	
	/* Attempt to load the appropriate file for the arguments.
	 */
	if ((return_code = print_file(filename)) == NOT_OK) {
		if (strlen(argv[0]) <= 8) {
			filename[12] = '2';
			if ((return_code = print_file(filename)) == OK) {
				return;
			}
		}
		
		print(
			"\n\tThe Help file could not be opened.  Either the command does "
			"not exist, or\n\ttoo many files are currently open.\n\n",
			TRUE
		);
	} else if (return_code == ERR_GEN_OUTOFMEMORY) {
		print(
			"\n\tThere is not enough free memory to hold that file right "
			"now.\n\n",
			TRUE
		);
	}
}

// Load
// Parameters: argc, argv - process name
// Return: void
// Functions called: program_load
// Loads a program into the suspended queue
void Load (int argc, char **argv) {
	int priority = atoi(argv[1]);
	char *filename = argv[0];
	int processState = APPLICATION; 
	program_load(priority, filename, processState);
	argc = argc;
}

// Quit
// Parameters: argc, argv - not needed to use this command.
// Returns: void.
// Uses the system function sys_req to make sure that the user
// wants to exit the system.
// Exits the system.
void Quit(int argc, char **argv) {
	char buf[BUFSIZ] = "";
	int  read = BUFSIZ;
	
	argc = argc;
	argv = argv;
	
	print("\n\tAre you sure you want to exit? (y/*)    > ", TRUE);
	sys_req(READ, TERMINAL, buf, &read);
	
	if (strcmp(buf, "y\n") == 0) {
		quitting = TRUE;
	}
}


// Resume
// Paramaters: none
// Returns: nothing.
// This aglorithm retrieves user input then uses the Find_PCB, Insert_PCB, and Remove_PCB.
// Removes the process from the suspended state. 
void Resume(int argc, char **argv){

	char *pcb_Name = argv[0];
	int errorState;
	ppcb myPCB;

	if(argc != 1){
		print("Resume takes one argument, Process name.\n", TRUE);
		return;
	}
	if((errorState = pcb_resume(pcb_Name)) == ERR_PQUEUE_ITEMNOTFOUND){
		char buffer[BUFSIZ] = "";
		sprintf(
				buffer, 
				"Process %s not found.",
				pcb_Name
				);
		print(buffer, TRUE);
	}
	else if(errorState == ERR_CHANGE_OF_SYSTEM_PROCESS){
		char buffer[BUFSIZ] = "";
		sprintf(
				buffer,
				"Can not change system process %s.",
				pcb_Name
				);
		print(buffer, TRUE);
	}
	else if(errorState == OK){
		char buffer[BUFSIZ] = "";
		sprintf(
		buffer,
		"Process %s was successfully moved to the ready queue.",
		pcb_Name
		);		
		print(buffer, TRUE);	
	}
}

// Set_Priority
// Paramaters: none
// Returns: nothing.
// This aglorithm finds the PCB using the Find_PCB command, removes it from its
// current position using the Remove_PCB command, then inserts it into its new position
// using the Insert_PCB command. 
// Changes the position of the PCB in it's queue based on it's new priority.
void Set_Priority(int argc, char **argv) {

	ppcb temp;
	char *pcb_Name = argv[0];
	int errorState;
	int priority = atoi(argv[1]);

	if (argc != 2) {
		print("Invalid number of arguments. Usage: Set_Priority process_name priority.\n",
				TRUE
		);
		return;
	}		
	if (priority < -128 || priority > 127) {
		print("Priority should be in the range -128 to 127\n", TRUE);
		return;
	}
	if((errorState = pcb_set_priority(pcb_Name, priority)) == ERR_PQUEUE_ITEMNOTFOUND){
		char buffer[BUFSIZ] = "";
		sprintf(
				buffer, 
				"Process %s not found.",
				pcb_Name
				);
		print(buffer, TRUE);
	}
	else if(errorState == ERR_CHANGE_OF_SYSTEM_PROCESS){
		char buffer[BUFSIZ] = "";
		sprintf(
				buffer,
				"Can not change system process %s.",
				pcb_Name
				);
		print(buffer, TRUE);
	}
	else if(errorState == OK){
		char buffer[BUFSIZ] = "";
		sprintf(
				buffer,
				"Priority was successfully set for process %s to %d.\n", 
				argv[0], 
				priority
				);
		print(buffer, TRUE);
	}
}


// Show_All
// Paramaters: none
// Returns: nothing.
// Functions called: pcb_show_ready, pcb_show_blocked,
// pcb_show_suspended, pring
// This aglorithm calls each of the other show commands. 
// Shows all processes in all queues. 
void Show_All(int argc, char **argv) {
	if (pcb_show_ready() == OK) {
		print("\n", FALSE);
	} else {
		print("\nThere are no processes in the ready queue.\n", FALSE);
	}
	
	if (pcb_show_blocked() == OK) {
		print("\n", FALSE);
	} else {
		print("\nThere are no processes in the blocked queue.\n", FALSE);
	}
	
	if (pcb_show_suspended() == OK) {
		print("\n", FALSE);
	} else {
		print("\nThere are no processes in the suspended queue.\n", FALSE);
	}
	
	print("\n", TRUE);
	argc = argc;
	argv = argv;
}

// Show_Blocked
// Paramaters: none
// Returns: nothing.
// Functions called: pcb_show_blocked, print
// This aglorithm loops through the blocked queue to print each process.
// Shows all processes in the blocked state.
void Show_Blocked(int argc, char **argv) {
	int errorState;
	argc = argc;
	argv = argv;

	if((errorState = pcb_show_blocked()) == OK){
		print("End of Blocked Queue\n", TRUE);
	}
	else if(errorState == ERR_PQUEUE_EMPTYQUEUE){
		print("There are no processes currently in the Blocked Queue\n", TRUE);
	}
	else if(errorState == ERR_GEN_OUTOFMEMORY){
		print("Out of memory\n", TRUE);
	}
}

// Show_PCB
// Paramaters: process name
// Returns: nothing.
// Functions calle: pcb_show, print
// This aglorithm retrieves input from the user to print the PCB's information.
// Shows a particular PCB.
void Show_PCB (int argc, char **argv) {
	char *pcb_Name = argv[0];
	int errorState;
	ppcb myPCB;

	if (argc != 1) {
		print(
			"Invalid number of arguments. Usage: Show_PCB processName\n",
			TRUE
		);
		return;
	}
	if((errorState = pcb_show(pcb_Name)) == ERR_PQUEUE_ITEMNOTFOUND){
		char buffer[BUFSIZ] = "";
		sprintf(
				buffer,
				"Process %s not found.",
				pcb_Name
				);
		print(buffer, TRUE);
	}
	else if(errorState == OK){
		print("", TRUE);
	}

}

// Show_Ready
// Paramaters: argc, argv - not used
// Returns: nothing.
// Functions called: pcb_show_ready, print
// This aglorithm loops through the ready queue to print each process.
// Shows all processes in the ready state.
void Show_Ready(int argc, char **argv) {
	int errorState;
	argc = argc;
	argv = argv;

	if((errorState = pcb_show_ready()) == OK){
		print("End of Ready Queue\n", TRUE);
	}
	else if(errorState == ERR_PQUEUE_EMPTYQUEUE){
		print("There are no processes currently in the Ready Queue\n", TRUE);
	}
	else if(errorState == ERR_GEN_OUTOFMEMORY){
		print("Out of memory\n", TRUE);
	}
}

// Show_Suspended
// Paramaters: argc, argv - not used
// Returns: nothing.
// This aglorithm loops through the suspended ready queue to print each process.
// Shows all processes in the suspended ready state.
void Show_Suspended(int argc, char **argv){
	int errorState;
	argc = argc;
	argv = argv;

	if((errorState = pcb_show_suspended()) == OK){
		print("End of Suspended Queue\n", TRUE);
	}
	else if(errorState == ERR_PQUEUE_EMPTYQUEUE){
		print(
			"There are no processes currently in the Suspended Queue\n",
			TRUE
		);
	}
	else if(errorState == ERR_GEN_OUTOFMEMORY){
		print("Out of memory\n", TRUE);
	}
}

// Suspend
// Paramaters: none
// Returns: nothing.
// Functions called: suspend_process, print
// This aglorithm makes use of the Insert_PCB, Remove_PCB, and the Find_PCB functions. 
// Places the process in the suspended queue. 
void Suspend(int argc, char **argv){

	char *pcb_Name = argv[0];
	int errorState;
	ppcb myPCB;

	if(argc != 1){
		print("Suspend takes one argument, Process name.\n", TRUE);
		return;
	}
	
	suspend_process(pcb_Name);
}

// Terminate
// Parameters: process Name
// Return: void
// Functions called: kill_process
// Terminates an application process from any queue
void Terminate(int argc, char **argv) {
	int  error_len;
	char *procName = argv[0];

	/* If the number of arguments passed is not one, then there is a problem.
	 */
	if (argc != 1) {
		print("\n\tError: Incorrect syntax.  Try \"Help Terminate\".\n\n", TRUE);
		return;
	}
	
	kill_process(procName);
}

// Version
// Parameters: argc, argv - neither are used to make this function run.
// Returns: void
// Functions called: print
// Displays the current version, along with the system authors.
void Version (int argc, char **argv) {
	argc = argc;
	argv = argv;

	if (argc > 0) {
		print("\n\tVersion takes no arguments.\n\n", TRUE);
	}
	
	print(
		"\n\tVERSION INFORMATION\n"
		"\n\tVersion: 6.0\n"
		"\n\tDeliverable: R6\n"
		"\n\tAuthors:\n"
		"\n\t\tRebecca Rush\n"
		"\n\t\tJoseph Craig\n"
		"\n\t\tSteven Andryzcik\n"
		"\n\tLast Modified: 12-08-2011\n\n",
		TRUE
	);
}